#!/usr/bin/python2.5
#
# Copyright 2011 Alexandr Kalenuk.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#   http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

""" emptie - API for using file system as an hierarchial data storage.

    Works with native python lists and dictionaries.
    Includes selfexplainatory functions:

    - clean(where)
    - add(where, what)
    - set(where, what)
    
    - get_one(where)
    - get_list(where)
    - get_dict(where)
"""

__authors__ = [
  '"Alexandr Kalenuk" <akalenuk@gmail.com>'
]

import os


def __add_one(where, what):
    path=where.split("/")
    for i in range(len(path)):
        spath="/".join(path[:i+1])
        if not os.path.exists(spath):
            os.makedirs(spath)
    spath=where+"/"+what;
    if not os.path.exists(spath):
        os.makedirs(spath)

  
def __set_one(where, what):
    clean(where)
    __add_one(where, what)


def __list_to_dict(what):
    if type(what)!=type([]):
        return what
    else:
        t_dict={}
        for i in range(len(what)):
            t_dict[i+1]=__list_to_dict(what[i])
        return t_dict

def __dict_to_list(what):
    if type(what)!=type({}):
        return what
    else:
        t_list=[0]*len(what)
        for i in what.items():
            t_list[int(i[0])-1]=__dict_to_list(i[1])
        return t_list
    

def __add_dict(where, what):
    if type(what)!=type({}):
        __add_one(where, str(what))
    else:
        for pair in what.items():
            __add_dict(where+"/"+str(pair[0]), pair[1])


def __set_dict(where, what):
    clean(where)
    __add_dict(where, what)


def clean(where, and_burn=False):
    ''' Cleans a branch
      
        Args:
            where: Path to a branch
            and_burn: Indicates that not only the contents, but the branch
                    itself is to be removed. 

        Returns:
            Nothing
    '''
    if os.path.exists(where):
        everyone=os.listdir(where)
        for one in everyone:
            clean(where+"/"+one, True)
        if and_burn:
            os.rmdir(where)

    
def get_everyone(where):
    ''' Gets a list of strins from a node
      
        Args:
            where: Path to a node
            
        Returns:
            List of strings
    '''
    return os.listdir(where)


def get_one(where):
    ''' Gets a string from a node
      
        Args:
            where: Path to a node
            
        Returns:
            One string

        Throws:
            'get_one found more than one' if there are more than one string.
    '''
    everyone=os.listdir(where)
    if len(everyone)==1:
        return everyone[0]
    else:
        raise Exception('get_one found more than one')
    

def get_dict(where):
    ''' Gets a dictionary
      
        Args:
            where: Path to a node
            
        Returns:
            A dictionary
    '''
    t_dict={}
    ls=os.listdir(where)
    if len(ls)==0:
        return ""
    elif len(ls)==1:
        return ls[0]
    else:
        for d in ls:
            t_dict[d]=get_dict(where+"/"+d)
        return t_dict


def get_list(where):
    ''' Gets a list
      
        Args:
            where: Path to a node
            
        Returns:
            A list
    '''
    got_dict=get_dict(where)
    return __dict_to_list(got_dict)


def add(where, something):
    ''' Adds a string (or something convertible), list or a dictionary
        to a node.
      
        Args:
            where: Path to a node
            
        Returns:
            Nothing
    '''
    if type(something)==type([]):
        __add_dict(where, __list_to_dict(something))
    elif type(something)==type({}):
        __add_dict(where, something)
    else:
        __add_one(where, str(something))


def set(where, something):
    ''' Sets a string (or something convertible), list or a dictionary
        in a node. Previous data will be lost.
      
        Args:
            where: Path to a node
            
        Returns:
            Nothing
    '''
    clean(where)
    add(where, something)


    
if __name__ == '__main__':
    # a little stress test
    lotta=range(1000)  
    import time

    try:
        ct=time.time()
        set("test/d1", lotta)
        print time.time()-ct
        DT=get_list("test/d1")
        print time.time()-ct

    except Exception as e:
        print e
